﻿-- ===========================================================================
-- = Quest Spam.lua Version 1.1.1
-- ===========================================================================
-- = by Wizwonk
-- =
-- = Credits: The inspiration came from FastQuest Classic
-- =          Thanks goes to:
-- =          My Brother who through contunious updates helped me with 
-- =				features and bugs.
-- =          Gold Tracker developer : I derived my commenting scheme from you 
-- =
-- ===========================================================================

-- ========= Changes =========================================================
-- v0.2.1 Nov 06, 2008  alpha stage, basic features in place, but has no options menu
-- v0.2.2 Nov 08, 2008  Options Menu added and functional
-- v0.3.0 Nov 09, 2008  FIX: QuestComplete XP calculated post turn-in
--						FIX: Moved some rogue loc text into the loctext file
-- v1.0.1 Nov 18, 2008  FIX: Debug mode now defaulted to off.
-- v1.1.0 Dec 27, 2011  UPDATE: Update to newest version of wow and prepping for re-release.


-- ========= Globals =========================================================
QS_DEBUG = false;
QuestSpamLog = {};
QuestSpamPref = {};
SLASH_QuestSpam1 = "/questspam";
SLASH_QuestSpam2 = "/qspam";
QuestSpamLastMsg = "";
local QSVer = GetAddOnMetadata("QuestSpam", "Version");

-- ========= Flags ===========================================================
QS_VariablesLoaded  = false;
QS_OptionsLoaded    = false;
QS_IsTestOn         = false;

-- ========= Frames ==========================================================
QuestSpam = CreateFrame("Button");
QuestSpam:Hide();
QuestSpam.questComplete = 
{ 
	xpgained = "", 
	iscompleted = false,
}

QuestSpam:SetFrameStrata("TOOLTIP")

-- ========= Defaul Preferences ==============================================
QuestSpamBasePref =
{
	["Version"]     = QSVer,
	["on"] 			= true,
	["party"]		= true,
	["raid"] 		= false,
	["guild"] 		= false,
	["channel"] 	= false,
	["channelid"] 	= "",
	["local"] 		= false,
	["achievement"] = true,
	["details"] 	= true,
	["discovery"] 	= true,
	["questxp"]     = true,
	["levelup"]		= true,
	["killxp"]		= false,    -- TODO: hanlde rested : Ashenvale Bear dies, you gain 270 experience (+135 exp Rested Bonus)
	["debug"]		= false,
};

if ( QS_DEBUG ) then
	QuestSpamPref = { ["debug"] = QS_DEBUG, };
end


-- ========= Functions and Handlers ==========================================

-- ===========================================================================
-- NAME : QSTrace(msg)
-- DESC : Logs a message to the DEFAULT_CHAT_FRAME for debug purposes
-- PARAMS
-- msg  : string : message to send to the DEFAULT_CHAT_FRAME
-- ===========================================================================
function QSTrace(msg)
	if (QuestSpamPref["debug"]) then
		QuestSpamLog[#QuestSpamLog+1] = msg;
		DEFAULT_CHAT_FRAME:AddMessage("|cffffaa11[QS]:" .. msg);
	end
end

QSTrace("Ver : " .. QSVer);
-- ===========================================================================
-- NAME : QSError(msg)
-- DESC : Sends any lua error messages to the DEFAULT_CHAT_FRAME
-- PARAMS
-- msg  : string : error message
-- ===========================================================================
function QSError(msg)
	DEFAULT_CHAT_FRAME:AddMessage("|cffff0000!QSERR:" .. msg);
end

-- native: this will setup the error event handler
seterrorhandler(QSError);  

-- ===========================================================================
-- NAME : QuestSpam_Chat(msg)
-- DESC : Used for sending confirmation to the DEFAULT_CHAT_FRAME
-- ===========================================================================
function QuestSpam_Chat(msg)
	DEFAULT_CHAT_FRAME:AddMessage("|cffffffff" .. msg);
end

-- Sets up a quick command Chat for sending feedback/confirmation
local Chat = QuestSpam_Chat;

-- ===========================================================================
-- NAME : QuestSpam:TogglePrefFlag(flagname)
-- DESC : Used for sending confirmation to the DEFAULT_CHAT_FRAME
-- PARAMS
-- flagname : string : flag that you would like to toggle
-- ===========================================================================
function QuestSpam:TogglePrefFlag(flagname)
	-- Ensure that we have a correct flagname
	if (QuestSpamPref[flagname] ~= nil) and (type(QuestSpamPref[flagname]) == "boolean" ) then
		QuestSpamPref[flagname] = (not QuestSpamPref[flagname]);
	end
end

-- ===========================================================================
-- NAME : QuestSpam:SpamMessage(msg,event)
-- DESC : This does the spamming and sends the messages to the correct location
-- PARAMS
-- msg   : string : The message that that will be spammed
-- event : string : The event is the reason the message was spammed (optional)
-- ===========================================================================
function QuestSpam:SpamMessage(msg,event)
	QuestSpamLastMsg = msg; -- This is mainly for test purposes
	local SendChat = SendChatMessage;
	if (QS_IsTestOn) then
		SendChat = function(chatmsg)
			DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00" .. msg);
		end
	end

	if (msg and QuestSpamPref["on"]) then
		if (QuestSpamPref["local"]) then
			SendChat(msg, "SAY");
		end
		if (QuestSpamPref["party"] and GetNumPartyMembers() > 0 ) then
			SendChat(msg, "PARTY");
		end
		if (QuestSpamPref["raid"] and GetNumRaidMembers() > 0) then
			SendChat(msg, "RAID");
		end
		if (QuestSpamPref["guild"]) then
			SendChat(msg, "GUILD");
		end
		if (QuestSpamPref["channel"]) then 
			id, name = GetChannelName(QuestSpamPref["channelid"]);
			if (id > 0 and name ~= nil) then
			  SendChat(msg, "CHANNEL", nil, id);
			end
			-- TODO: Achievements will have seperate rules for where they are spammed.
			-- It's cool to send your Achievment to your party so you can link it to them
			if (event and event == "ACHIEVEMENT_EARNED" and not QuestSpamPref["party"] and GetNumPartyMembers() > 0 ) then
			  SendChat(msg, "PARTY");
			end
		end
	end
end

-- ===========================================================================
-- NAME : QuestSpam:ParseMessage(msg)
-- DESC : This is used to parse the UI_INFO_MESSAGE to determine if it's worth sending
-- PARAMS
-- msg   : string : The message that that will be parsed
-- ===========================================================================
function QuestSpam:ParseMessage(msg)
	for index, value in pairs(QUESTSPAM_UI_INFO_MATCH) do
		if ( string.find(msg, value) ) then
			-- nice to have some bools hanging around for later usage
			local questxp = (value == QUESTSPAM_UI_INFO_MATCH[5]);
			local discovery = (value == QUESTSPAM_UI_INFO_MATCH[6]);
			local killxp = (value == QUESTSPAM_UI_INFO_MATCH[7]);

			-- Early return if the option is not enabled
			if (questxp and not QuestSpamPref["questxp"]) then 
				break;
			elseif (discovery and not QuestSpamPref["discovery"]) then
				break;
			elseif (killxp and not QuestSpamPref["killxp"]) then
				break;
			else
				
				if (questxp) then
					QuestSpam.questComplete.xpgained = string.gsub(msg,QUESTSPAM_EXP_GAINED,"%1");
					QuestSpam.questComplete.iscompleted = true;
					break;
				end

				if (killxp) then
					-- Lets change the message so it's shorter
					msg = string.gsub(msg,QUESTSPAM_KILLEXP_MATCH,QUESTSPAM_KILLEXP_MESSAGE);
				end
				
				-- TODO: maybe I should setup another variable to be safe?
				-- the message sent (msg) is modified at this point; 
				QuestSpam:SpamMessage(msg);
			end
			break;
		end
	end
end


-- ===========================================================================
-- NAME : QuestSpam:OnEvent(event, message)
-- DESC : Event handling
-- PARAMS
-- event   : string : The event string (enum)
-- message : string : The message sent by the event
-- ===========================================================================
function QuestSpam:OnEvent(event, message)
	-- for debugging
	if (QuestSpamPref["debug"] and event) then
		tracemsg = message or "";
		QSTrace("OnEvent:(" .. event .. ", " .. tracemsg .. ")");
	end

	-- initialize QuestSpam
	if event == "VARIABLES_LOADED" then
		self:Initialize();
		--TODO: Is there a more elegant way of handling the options menu loading out of sync?
		QS_VariablesLoaded = true;
		if ( QuestSpamOptionsFrame ~= nil and not QS_OptionsLoaded) then
			QuestSpamLoadOptionValues();
		end
	end

	if (message and QuestSpamPref["on"]) then
		if (event == "UI_INFO_MESSAGE") then
			-- Quest Progress:
			local uQuestText = gsub(message, QUESTSPAM_QUESTTEXT, "%1", 1);
			if ( uQuestText ~= message) then
				if (QuestSpamPref["details"]) then
					QuestSpam:SpamMessage(QUESTSPAM_QUESTPROGRESS .. message);
				end
			else
				QuestSpam:ParseMessage(message);
			end	
		end
		
		if (event == "CHAT_MSG_COMBAT_XP_GAIN") then
			QuestSpam:ParseMessage(message);
		end

		if (event == "PLAYER_XP_UPDATE" and QuestSpam.questComplete.iscompleted) then
			QuestSpam:ShowExperience();
		end

		if (event == "ACHIEVEMENT_EARNED") then
			QuestSpam:ShowAchievement(message);
		end
		
		if (event == "PLAYER_LEVEL_UP" and QuestSpamPref["levelup"]) then
			-- message will be the new level #
			QuestSpam:SpamMessage(string.format(QUESTSPAM_LEVELUPMESSAGE,message));
		end
		
		if (event == "CHAT_MSG_SYSTEM") then
			QuestSpam:ParseMessage(message);
		end
	end
end

-- ===========================================================================
-- NAME : QuestSpam:ShowAchievement()
-- DESC : This will spam with the latest or current achievement and link.
-- PARAMS
-- achievementId   : string : The achievementid that you want info about, if not sent, the latest achievement will be used
-- ===========================================================================
function QuestSpam:ShowAchievement(achievementId)
	-- New Achievement! [Murloc Hater] for 10 pts for total of 20 Achievements at 200 pts
	-- TODO: Use string.format instead of concatenation
	local ach1,ach2,ach3,ach4,ach5;
	local messageFormat = QUESTSPAM_ACHIEVEMENT_MESSAGE;

	if (achievementId == nil) then
		ach1,ach2,ach3,ach4,ach5 = GetLatestCompletedAchievements();
		achievementId = ach1;
		messageFormat = QUESTSPAM_LATESTACHIEVEMENT .. QUESTSPAM_ACHIEVEMENT_MESSAGE;
	end
	
	local IDNumber, Title, Points, Completed, Month, Day, Year, Description, Flags, Image, RewardText = GetAchievementInfo(achievementId);
	local achievementLink = GetAchievementLink(achievementId) or Title or string.format("[unknown $s",tostring(achievementId));
	local achTotal, achCompleted = GetNumCompletedAchievements();
	
	QuestSpam:SpamMessage(string.format(messageFormat,achievementLink,(Points or 0),tostring(achCompleted),tostring(GetTotalAchievementPoints())));
end



-- ===========================================================================
-- NAME : QuestSpam:ShowExperience()
-- DESC : Called when the PLAYER_XP_UPDATE event occurs and quest completed
-- ===========================================================================
function QuestSpam:ShowExperience()
	-- This allows me to show the percentage of the current level when you turn in a quest
	-- TODO: possible feature is choosing to show percent to next level
	local messagePrefix = ""; 
	local nextLevel = UnitLevel("player")+1
	if nextLevel > 80 then nextLevel = 80 end
	local totalXP = UnitXPMax("player");
	local currentXP = UnitXP("player");
	local toLevelXP = totalXP - currentXP;
	local currentXPPercent = math.floor(currentXP / totalXP * 10000) / 100;
	local toLevelXPPercent = math.floor(toLevelXP / totalXP * 10000) / 100;
	
	if QuestSpam.questComplete.iscompleted then
		-- Message : Gained 50 XP and is at 40% of level 3
		messagePrefix = string.format(QUESTSPAM_EXP_MESSAGE_1, QuestSpam.questComplete.xpgained);
		QuestSpam.questComplete.iscompleted = false;
	end

	local messageFormat = string.format(QUESTSPAM_EXP_MESSAGE_2,messagePrefix, tostring(currentXPPercent),tostring(UnitLevel("player")));
	QuestSpam:SpamMessage(messageFormat);

end

-- ===========================================================================
-- NAME : QuestSpam:Initialize()
-- DESC : Called when the VARIABLES_LOADED event occurs
-- ===========================================================================
function QuestSpam:Initialize()
	QSTrace("Starting log at position:" .. #QuestSpamLog .. " : " .. UnitName("player"));

	QuestSpam:LoadDefaults();
	
	-- Welcome Message ; Also good for knowing if QSpam was loaded (!Swatter can knock error message out)
	DEFAULT_CHAT_FRAME:AddMessage("|cffffaa11Quest Spam " .. QuestSpamPref["Version"] .. " loaded");


	-- Register / Unregister for events
	self:UnregisterEvent("VARIABLES_LOADED");
	self:RegisterEvent("CHAT_MSG_SYSTEM");
	self:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN");
	self:RegisterEvent("PLAYER_LEVEL_UP");
	self:RegisterEvent("ACHIEVEMENT_EARNED");
	self:RegisterEvent("PARTY_MEMBERS_CHANGED");
	self:RegisterEvent("QUEST_PROGRESS");
	self:RegisterEvent("QUEST_COMPLETE");
	self:RegisterEvent("UI_INFO_MESSAGE");
	self:RegisterEvent("PLAYER_XP_UPDATE");
end

-- ===========================================================================
-- NAME : QuestSpam:LoadDefaults()
-- DESC : This will reload the default values into the current global
-- ===========================================================================
function QuestSpam:LoadDefaults()
	-- Setup Defaults
	for key, def in pairs(QuestSpamBasePref) do
		if QuestSpamPref[key] == nil then
		  QuestSpamPref[key] = def
		end
	end

	if (QSVer ~= QuestSpamPref["Version"]) then
		-- Do version updates here
        QSTrace("Verion Updated to " .. QSVer);
		QuestSpamPref["Version"] = QSVer;
	end

	QuestSpamPref["debug"] = false;

end

-- ===========================================================================
-- NAME : anonymous function(text)
-- DESC : Slash handler for slash commands
-- ===========================================================================
SlashCmdList["QuestSpam"] = function(text)
	if (text) then
		-- TODO: single commands only right now, need to update to multiple commands
		local cmd = gsub(text, "%s*([^%s]+).*", "%1");
		QSTrace("CMD:"..cmd);
		
		-- automatic handling of toggle parameters
		for key, def in pairs(QuestSpamPref) do
			if (cmd == key and type(QuestSpamPref[key]) == "boolean" ) then
				QuestSpam:TogglePrefFlag(cmd);
				Chat(cmd .. " is now : " .. tostring(QuestSpamPref[cmd]));
				QuestSpamLoadOptionValues();
			end
		end

		-- handler for the channel id
		-- TODO: Determine if I can set channels by name.
		if (cmd == "channelid") or (cmd == "spamchannelid") then
			local trychannelid = gsub(text, cmd .. "%s*([^%s]+).*", "%1");
			QSTrace("Trying to set channel: " .. trychannelid);
			if gsub(text, cmd .. "%s*([^%s]+).*", "%1") then
				QuestSpamPref["channelid"] = trychannelid;
				Chat(cmd .. " is now : " .. tostring(QuestSpamPref["channelid"]));
				QuestSpamLoadOptionValues();
			end
		end

		if (cmd == "stats") then
			QuestSpam:ShowExperience();	
			QuestSpam:ShowAchievement(nil);	
		end
		
		if (cmd == "forcereset") then
			QuestSpamForceReset();	
		end

		if (cmd == "test") then
			QuestSpamRunTests();
		end

		if (cmd == "xp") then
			QuestSpam:ShowExperience();
		end
		
		-- The help message
		-- TODO: I need a better help message
		if (cmd == "" or cmd == "status") then
			Chat("Quest Spam Status");
			for key, def in pairs(QuestSpamPref) do
				Chat("qspam " .. string.lower(key) .. " = " .. tostring(QuestSpamPref[key]));
			end
		end

	end
end

-- ===========================================================================
-- NAME : QuestSpam_RunTests()
-- DESC : This will run my test cases for messaging
-- ===========================================================================
function QuestSpamRunTests()
	QS_IsTestOn = true;
	QSTrace("Start Tests");

	QSTrace("TEST : Quest Accepted");
	QuestSpam:OnEvent("CHAT_MSG_SYSTEM", "Quest accepted: Healing the Lake");

	QSTrace("TEST : Quest Completed");
	QuestSpam:OnEvent("CHAT_MSG_SYSTEM", "Investigate Echo Ridge completed.");
	QuestSpam:OnEvent("CHAT_MSG_SYSTEM", "Volatile Mutations completed.");

	QSTrace("TEST : Quest exp");
	QuestSpam:OnEvent("CHAT_MSG_SYSTEM", "Experience gained: 170.");

	QSTrace("TEST : Discovery");
	QuestSpam:OnEvent("UI_INFO_MESSAGE", "Discovered: Echo Ridge Mine");

	QSTrace("TEST : QuestProgress");
	QuestSpam:OnEvent("UI_INFO_MESSAGE", "Kobold Workers slain: 1/10");
	QuestSpam:OnEvent("UI_INFO_MESSAGE", "Kobold Workers slain: 2/10");
	QuestSpam:OnEvent("UI_INFO_MESSAGE", "Kobold Workers slain: 10/10");

	QSTrace("TEST : Combat exp");
	QuestSpam:OnEvent("CHAT_MSG_COMBAT_XP_GAIN", "Vale Moth dies, you gain 26 experience.");
	QuestSpam:OnEvent("CHAT_MSG_COMBAT_XP_GAIN", "Ashenvale Bear dies, you gain 270 experience (+135 exp Rested Bonus)");

	QSTrace("TEST : Achievement");
	QuestSpam:OnEvent("ACHIEVEMENT_EARNED", "1017");
	
	QSTrace("End Tests");
	QS_IsTestOn = false;
end

-- ===========================================================================
-- NAME : QuestSpamForceReset()
-- DESC : This will force a reset on the preferences and log
-- ===========================================================================
function QuestSpamForceReset()
	QuestSpamLog = {};
	QuestSpamPref ={};
	QuestSpam:LoadDefaults();
	Chat("Log Cleared and Defaults Reset");
end

-- ========= Register Events  ================================================
QuestSpam:RegisterEvent("VARIABLES_LOADED");
QuestSpam:SetScript("OnEvent", QuestSpam.OnEvent);
